package cn.fu.ssh.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang.StringUtils;

import cn.fu.ssh.dao.ILeaveBillDao;
import cn.fu.ssh.domain.Employee;
import cn.fu.ssh.domain.LeaveBill;
import cn.fu.ssh.service.IWorkflowService;
import cn.fu.ssh.utils.SessionContext;
import cn.fu.ssh.web.form.WorkflowBean;

public class WorkflowServiceImpl implements IWorkflowService {
	/**请假申请Dao*/
	private ILeaveBillDao leaveBillDao;
	
	private RepositoryService repositoryService;
	
	private RuntimeService runtimeService;
	
	private TaskService taskService;
	
	private FormService formService;
	
	private HistoryService historyService;
	
	public void setLeaveBillDao(ILeaveBillDao leaveBillDao) {
		this.leaveBillDao = leaveBillDao;
	}

	public void setHistoryService(HistoryService historyService) {
		this.historyService = historyService;
	}
	
	public void setFormService(FormService formService) {
		this.formService = formService;
	}
	
	public void setRuntimeService(RuntimeService runtimeService) {
		this.runtimeService = runtimeService;
	}
	public void setTaskService(TaskService taskService) {
		this.taskService = taskService;
	}

	public void setRepositoryService(RepositoryService repositoryService) {
		this.repositoryService = repositoryService;
	}
	
	/**获取部署对象的集合List*/
	public List<Deployment> findDeploymentList() {
		List<Deployment> deploymentList = repositoryService.createDeploymentQuery()//创建部署对象的查询
									.orderByDeploymenTime().asc()//按照部署时间升序排列
									.list();
		return deploymentList;
	}
	
	/**查询流程定义的集合List*/
	public List<ProcessDefinition> findProcessDefinitionList() {
		List<ProcessDefinition> pdList = repositoryService.createProcessDefinitionQuery()//
									.list();
		return pdList;
	}

	/**部署流程定义，使用zip格式的输入流完成部署*/
	public void saveDeployment(String filename, File file) {
		
		try {
			//将File类型转换成zip格式
			ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(file));
			//2.部署流程定义，使用zip格式的输入流完成部署
			repositoryService.createDeployment()//创建部署对象
								.name(filename)//设置部署的名称
								.addZipInputStream(zipInputStream)//使用zip输入流
								.deploy();//完成部署
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	/**使用部署对象ID，级联删除流程定义*/
	public void deleteDeploymentByID(String deploymentId) {
		repositoryService.deleteDeployment(deploymentId, true);	
	}
	
	/**使用资源图片名称和部署ID，查询资源文件表，获取存放图片的输入流InputStream*/
	public InputStream findInputStreamByImageNameAndDeploymentId(
			String imageName, String deploymentId) {
		InputStream inputStream = repositoryService.getResourceAsStream(deploymentId, imageName);
		return inputStream;
	}
	
	/**启动流程实例，并设置请假单的状态*/
	public void saveStartProcessInstance(Long id) {
		//1：获取请假单ID，使用请假单ID，查询请假单对象LeaveBill
		LeaveBill leaveBill = leaveBillDao.findLeaveBillByID(id);
		if(leaveBill!=null){
			//存放流程变量的集合
			Map<String, Object> variables = new HashMap<String, Object>();
			//2：更新LeaveBill对象，将状态从0更新到1，初始录入变成审核中
			leaveBill.setState(1);//快照更新
			//3：从当前Session中获取用户信息，使用流程变量指定下一个任务的办理人${inputUser}
			Employee employee = SessionContext.get();
			variables.put("inputUser", employee.getName());
			//4：获取请假单对象的名称LeaveBill（表示流程定义的key）
			String classType = leaveBill.getClass().getSimpleName();
			
			//业务对象ID（格式：LeaveBill.id），表示业务
			String objId = classType+"."+id;
			//使用流程变量，添加业务对象ID，使得业务关联流程
			
			variables.put("objId", objId);
			
			String processDefinitionKey = classType;
			//5：启动流程实例，按照流程定义的key启动流程实例，并设置流程变量，同时设置业务key（对应BUSINESS_KEY），其中objId表示业务key
			runtimeService.startProcessInstanceByKey(processDefinitionKey,objId,variables);
		}
		
	}
	
	/**指定个人任务的办理人，查询个人任务列表*/
	public List<Task> findTasklistByUser(String name) {
		List<Task> list = taskService.createTaskQuery()//创建任务查询对象
					.taskAssignee(name)//指定个人任务的办理人
					.orderByTaskCreateTime().desc()//按照时间降序排列
					.list();
		return list;
	}

	/**使用任务ID，查询任务的表单对象*/
	public TaskFormData findTaskFormData(String taskId) {
		TaskFormData taskFormData = formService.getTaskFormData(taskId);
		return taskFormData;
	}

	/**使用任务ID，获取执行对象中存放的业务Key字段的值，从而获取请假申请表单ID*/
	public String findObjIdByTaskId(String taskId) {
		//1：使用任务ID，查询任务对象
		Task task = taskService.createTaskQuery()//
							.taskId(taskId)//使用任务ID的主键查询
							.singleResult();
		//2：使用流程实例ID，查询正在执行的流程实例，返回流程实例对象
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
							.processInstanceId(task.getProcessInstanceId())//使用流程实例ID查询
							.singleResult();
		//3:从流程实例对象中获取BUSINESS_KEY（格式：LeaveBill.3）
		String businessKey = pi.getBusinessKey();
		//请假单ID
		String objId = "";
		if(StringUtils.isNotBlank(businessKey)){
			objId = businessKey.split("\\.")[1];
		}
		return objId;
	}
	
	/**使用任务ID，获取当前任务连线的名称，将连线的名称放置到List<String>*/
	public List<String> findOutcomeNameListByTaskId(String taskId) {
		//定义连线的名称集合
		List<String> list = new ArrayList<String>();
		//1：使用任务ID，查询任务对象
		Task task = taskService.createTaskQuery()//
							.taskId(taskId)//使用任务ID的主键查询
							.singleResult();
		//2:获取流程定义的实体对象，因为这里封装了leaveBill.bpmn文件中的内容
		//获取流程定义ID
		String processDefinitionId = task.getProcessDefinitionId();
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
		//获取正在执行的流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//使用流程实例ID，查询正在执行的流程实例，返回流程实例对象
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
							.processInstanceId(processInstanceId)//使用流程实例ID查询
							.singleResult();
		//获取当前活动ID（activityId）
		String activityId = pi.getActivityId();
		ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);//使用当前活动ID，查询活动对象
		//获取连线的名称，连线的名称封装到List<PvmTransition>
		List<PvmTransition> pvmList = activityImpl.getOutgoingTransitions();
		if(pvmList!=null && pvmList.size()>0){
			for(PvmTransition pvmTransition:pvmList){
				//使用leaveBill.bpmn文件中的属性名称name获取对应属性名称的值
				String outcomeName = (String) pvmTransition.getProperty("name");
				if(StringUtils.isNotBlank(outcomeName)){
					list.add(outcomeName);
				}
			}
		}
		return list;
	}
	
	/**保存审核记录*/
	public void saveSubmitTask(WorkflowBean workflowBean) {
		//任务ID
		String taskId = workflowBean.getTaskId();
		//连线的名称
		String outcome = workflowBean.getOutcome();
		//批注信息
		String comment = workflowBean.getComment();
		/**
		 * 1：使用任务ID，同时使用流程变量（设置任务连线的名称用来完成任务），完成当前人的个人任务
			  * 流程变量的名称：outcome
			  * 流程变量的值，页面传递的按钮的value属性值
		 */
		//完成任务的同时设置流程变量
		Map<String, Object> variables = new HashMap<String,Object>();
		variables.put("outcome", outcome);
		//2：完成任务之前，使用类的方式设置下一个任务的办理人（做过）
		//3：使用页面传递的批注信息，添加批注字段，用于对审核记录的查询
		//使用任务ID，查询任务对象
		Task task = taskService.createTaskQuery()//
							.taskId(taskId)//使用任务ID的主键查询
							.singleResult();
		//流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//添加认证（表示当前任务的办理人添加的认证，如果不加，将会看到act_hi_comment表中的user_id是空值）
		Authentication.setAuthenticatedUserId(SessionContext.get().getName());
		//添加批注
		taskService.addComment(taskId, processInstanceId, comment);//参数三，添加的批注信息
		//完成任务
		taskService.complete(taskId, variables);
		/**
		 * 4：如果完成任务之后，流程结束（即当前任务是最后一个任务的时候）
		 	* 更新请假申请单的状态从审核中变成审核完成（state从1变成2）
		 */
		//判断流程是否结束（获取流程实例对象）
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
						.processInstanceId(processInstanceId)//使用流程实例ID查询
						.singleResult();
		//说明流程结束
		if(pi==null){
			//获取请假单ID
			Long id = workflowBean.getId();
			//查询请假单对象
			LeaveBill leaveBill = leaveBillDao.findLeaveBillByID(id);
			//快照更新
			leaveBill.setState(2);//2表示审核已完成
		}
	}
	
	/**使用任务ID，查询每个任务提交的批注（目的：用来查询领导者的审核信息）*/
	public List<Comment> findCommentListByTaskId(String taskId) {
		//用来存放批注
		List<Comment> list = new ArrayList<Comment>();
		//使用任务ID，查询任务对象
		Task task = taskService.createTaskQuery()//
							.taskId(taskId)//使用任务ID的主键查询
							.singleResult();
		//获取流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//使用流程实例，查询活动的历史表，获取当前流程实例对应的任务ID（已完成），才能获取以完成的批注
		List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()//创建历史活动查询
									.processInstanceId(processInstanceId)//按照流程实例ID查询
									.activityType("userTask")//按照活动类型，userTask表示任务的活动
									.list();
		if(haiList!=null && haiList.size()>0){
			for(HistoricActivityInstance hai:haiList){
				String hTaskId = hai.getTaskId();
				//获取批注
				List<Comment> commentList = taskService.getTaskComments(hTaskId);
				if(commentList!=null && commentList.size()>0){
					list.addAll(commentList);//使用list存放所有的集合，目的将多个List放置到一个List中
				}
			}
		}
		return list;
	}
	
	/**使用任务ID，查询流程定义的对象*/
	public ProcessDefinition findProcessDefinitionByTaskId(String taskId) {
		//（1）使用任务ID，查询任务的对象
		Task task = taskService.createTaskQuery()//
							.taskId(taskId)//使用任务ID的主键查询
							.singleResult();
		//（2）从任务对象中，获取流程定义的ID
		String pdId = task.getProcessDefinitionId();
		//（3）用流程定义ID，查询流程定义对象
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()//
							.processDefinitionId(pdId)//使用流程定义的ID查询
							.singleResult();
		return pd;
	}
	
	/**使用任务ID，获取leaveBill文件中对应的节点活动的坐标*/
	public Map<String, Object> findCoordingByTaskId(String taskId) {
		/**
		 * 使用Map<String,Object>集合
			     key值定义的坐标的名称
			     value值对应真实的值
		 */
		Map<String,Object> map = new HashMap<String,Object>();
		//1：使用任务ID，查询任务对象
		Task task = taskService.createTaskQuery()//
							.taskId(taskId)//使用任务ID的主键查询
							.singleResult();
		//2:获取流程定义的实体对象，因为这里封装了leaveBill.bpmn文件中的内容
		//获取流程定义ID
		String processDefinitionId = task.getProcessDefinitionId();
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
		//获取正在执行的流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//使用流程实例ID，查询正在执行的流程实例，返回流程实例对象
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
							.processInstanceId(processInstanceId)//使用流程实例ID查询
							.singleResult();
		//获取当前活动ID（activityId）
		String activityId = pi.getActivityId();
		ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);//使用当前活动ID，查询活动对象
		//使用Map集合存放坐标
		map.put("x", activityImpl.getX());
		map.put("y", activityImpl.getY());
		map.put("width", activityImpl.getWidth());
		map.put("height", activityImpl.getHeight());
		return map;
	}
	
	/**使用请假单ID，查询Activiti中存放的审核记录Comment*/
	public List<Comment> findCommentListById(Long id) {
		//使用申请表单ID，查询申请表单的对象
		LeaveBill leaveBill = leaveBillDao.findLeaveBillByID(id);
		//获取返回业务的对象类名的名称
		String obj = leaveBill.getClass().getSimpleName();
		//用来存放批注
		List<Comment> list = new ArrayList<Comment>();
		//查询历史的流程变量
		HistoricVariableInstance hvi = historyService.createHistoricVariableInstanceQuery()//创建历史流程变量查询
									.variableValueEquals("objId", obj+"."+id)//是流程流程变量的名称和流程变量的值，查询流程变量的惟一记录
									.singleResult();
		//获取流程实例ID
		String processInstanceId = hvi.getProcessInstanceId();
		//使用流程实例，查询活动的历史表，获取当前流程实例对应的任务ID（已完成），才能获取以完成的批注
		List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()//创建历史活动查询
									.processInstanceId(processInstanceId)//按照流程实例ID查询
									.activityType("userTask")//按照活动类型，userTask表示任务的活动
									.list();
		if(haiList!=null && haiList.size()>0){
			for(HistoricActivityInstance hai:haiList){
				String hTaskId = hai.getTaskId();
				//获取批注
				List<Comment> commentList = taskService.getTaskComments(hTaskId);
				if(commentList!=null && commentList.size()>0){
					list.addAll(commentList);//使用list存放所有的集合，目的将多个List放置到一个List中
				}
			}
		}
		return list;
	}
}
